<?php

Class AccountConvert extends DataExtension implements PermissionProvider {
	public static function get_extra_config($class, $extension, $args) {
        return array(
            'db' => array(
                'Amount' => 'Currency',
                'Rate' => 'Percentage',
        		'Reference' => 'Varchar',
        		'IsRevert' => 'Boolean'
            ),
            'has_one' => array(
                'Member' => 'Member'
            ),
            'many_many' => array(
                'SendAccounts' => Config::inst()->get($extension, sprintf('%s.Send', $class)),
        		'ReceiveAccounts' => Config::inst()->get($extension, sprintf('%s.Receive', $class))
            ),
            'searchable_fields' => array(
                'Created' => array(
					'field' => 'DateField',
					'filter' => 'DateMatchFilter'
				),
		        'Member.Username',
		        'Member.FirstName',
		        'Member.Surname',
		        'Reference',
		        'Rate',
		        'Amount' => array(
		            'filter' => 'GreaterThanOrEqualFilter'
		        ),
		        'IsRevert'
            ),
            'summary_fields' => array(
                'Created.Nice',
		        'Member.Username',
		        'Member.Name',
		        'Type',
		        'Reference',
		        'Rate',
		        'Amount',
		        'IsRevert.Nice'
            ),
            'defaults' => array('Rate' => Config::inst()->get($extension, sprintf('%s.Rate', $class))),
            'default_sort' => 'Created DESC, ID DESC',
            'create_table_options' => array('MySQLDatabase' => 'ENGINE=InnoDB')
        );
    }
	
	/**
     * Generate reference for account transfer
     * @return str Returns the reference
     */
    static function reference_generator($account_class) {
        $reference = rand(1, 99999999);
        $reference = str_pad($reference, 8, "0", STR_PAD_LEFT);
        while($account_class::get()->filter('Reference', $reference)->count()) {
            $reference = rand(1, 999999999);
            $reference = str_pad($reference, 8, "0", STR_PAD_LEFT);
        }
        return $reference;
    }
	
	function updateFieldLabels(&$labels) {
		$labels['Created'] = _t('AccountConvert.DATE', 'Date');
		$labels['Created.Nice'] = _t('AccountConvert.DATE', 'Date');
		$labels['Amount'] = _t('AccountConvert.AMOUNT', 'Amount');
		$labels['Reference'] = _t('AccountConvert.REFERENCE', 'Reference');
		$labels['Type'] = _t('AccountConvert.TYPE', 'Type');
		$labels['Rate'] = _t('AccountConvert.RATE', 'Rate');
		$labels['IsRevert'] = _t('AccountConvert.IS_REVERT', 'Is Revert?');
		$labels['IsRevert.Nice'] = _t('AccountConvert.IS_REVERT', 'Is Revert?');
		$labels['Member.Username'] = _t('AccountConvert.USERNAME', 'Username');
		$labels['Member.Name'] = _t('AccountConvert.NAME', 'Name');
		$labels['Member'] = _t('AccountConvert.NAME', 'Name');
		$labels['Member.FirstName'] = _t('AccountConvert.FIRSTNAME', 'First Name');
		$labels['Member.Surname'] = _t('AccountConvert.SURNAME', 'Surname');
	}
	
	function validate(ValidationResult $validationResult) {
		if($this->owner->SetUsername && !$this->owner->MemberID){
			$this->owner->MemberID = Distributor::get_id_by_username($this->owner->SetUsername);
		}
		
		if(!$this->owner->MemberID) {
            $subvalid = new ValidationResult();
            $subvalid->error(_t('AccountConvert.INVALID_MEMBER_ID', 'Invalid member id'), 'INVALID_MEMBER_ID');
            $validationResult->combineAnd($subvalid);
        }
		
		if($this->owner->Rate <= 0) {
            $subvalid = new ValidationResult();
            $subvalid->error(_t('AccountConvert.INVALID_CONVERT_RATE', 'Invalid convert rate'), 'INVALID_CONVERT_RATE');
            $validationResult->combineAnd($subvalid);
        }
        
        if(!$this->owner->exists() && $this->owner->Amount <= 0){
			$subvalid = new ValidationResult();
            $subvalid->error(_t('AccountConvert.INVALID_CONVERT_AMOUNT', "Invalid convert amount"), "INVALID_CONVERT");
            $validationResult->combineAnd($subvalid);
		} else {
	        if(!$this->owner->exists() && $this->owner->Amount) {
	        	$account_class = $this->getSendAccountClass();
	            $balance = Account::get($account_class, $this->owner->MemberID)->Balance;
	            if($this->owner->Amount > $balance){
	                $subvalid = new ValidationResult();
	                $subvalid->error(_t('AccountConvert.INSUFFICIENT_BALANCE', 'Insufficient account balance ({balance})', 'Display insufficient account balance', array('balance' => DBField::create_field('Currency', $balance)->Nice())), "INSUFFICIENT_ACCOUNT_BALANCE");
	                $validationResult->combineAnd($subvalid);
	            }
	        }
			else if($this->owner->exists() && $this->owner->isChanged('IsRevert') && $this->owner->IsRevert){
				$account_class = $this->getReceiveAccountClass();
	            $balance = Account::get($account_class, $this->owner->MemberID)->Balance;
	            if($this->owner->Amount > $balance){
	                $subvalid = new ValidationResult();
	                $subvalid->error(_t('AccountConvert.INSUFFICIENT_BALANCE', 'Insufficient account balance ({balance})', 'Display insufficient account balance', array('balance' => DBField::create_field('Currency', $balance)->Nice())), "INSUFFICIENT_ACCOUNT_BALANCE");
	                $validationResult->combineAnd($subvalid);
	            }
			}
		}

        return $validationResult;
    }

	function updateCMSFields(FieldList $fields) {
        if($this->owner->exists()) {
            $fields->makeFieldReadonly('Amount');
            $fields->makeFieldReadonly('Reference');
            $fields->makeFieldReadonly('MemberID');
			$fields->removeByName('SendAccounts');
			$fields->removeByName('ReceiveAccounts');
        } else {
            $fields->insertBefore(UsernameField::create('SetUsername', _t('AccountConvert.USERNAME', 'Username')), 'Amount');
            $fields->push(HiddenField::create('AccountClass', 'AccountClass', Config::inst()->get($this->class, sprintf('%s.Send', $this->owner->class))));
			$fields->removeByName('Reference');
			$fields->removeByName('IsRevert');
            $fields->removeByName('MemberID');
        }
    }
    
    function getCMSValidator() {
        return AccountConvert_Validator::create();
    }

	function getConvertFormFields($memberid){
		$account_class = $this->getSendAccountClass();
		$rate = Config::inst()->get($this->class, sprintf('%s.Rate', $this->owner->class));
        $fields = FieldList::create(
            HiddenField::create('MemberID', 'MemberID', $memberid),
            HiddenField::create('AccountBalance', 'AccountBalance', Account::get($account_class, $memberid)->Balance),
            HtmlEditorField_Readonly::create('ShowAccountBalance', _t('AccountConvert.CURRENT_BALANCE', 'Current Balance'), Account::get($account_class, $memberid)->obj('Balance')->Nice()),
            HtmlEditorField_Readonly::create('ShowType', _t('AccountConvert.TYPE', 'Type'), $this->getType()),
            ConvertAmountField::create('Amount', _t('AccountConvert.AMOUNT', 'Amount'))->setRate($this->getRate()),
			SecurityPinField::create('AccountConvertSecurityPin', _t('AccountConvert.SECURITY_PIN', 'Security Pin'))
        );
        
        $this->owner->extend('updateConvertFormFields', $fields);
        
        return $fields;
    }
    
    function onBeforeWrite(){
        if($this->owner->SetUsername){
            $this->owner->setField('MemberID', Distributor::get_id_by_username($this->owner->SetUsername));
        }

		if($this->owner->Reference == '') {
            $this->owner->setField('Reference', self::reference_generator($this->owner->class));
        }
		
		if($this->owner->Rate == '') {
            $this->owner->setField('Rate', Config::inst()->get($this->class, sprintf('%s.Rate', $this->owner->class)));
        }
    }
    
    function onAfterWrite(){
    	$send_account_class = $this->getSendAccountClass();
		$receive_account_class = $this->getReceiveAccountClass();
        if($this->owner->Amount > 0 && $this->owner->Rate > 0){
            if(!$count = $this->owner->SendAccounts()->filter('Debit:GreaterThan', 0)->count()){
                $data = array(
                    'Type' => 'Convert',
                    'Debit' => $this->owner->Amount,
                    'Reference' => $this->owner->Reference,
                    'Description' => 'Account balance convert'
                );
				$id = $send_account_class::create_statement($data, $this->owner->MemberID);
                $this->owner->SendAccounts()->add($id);
            }
			
			if(!$count = $this->owner->ReceiveAccounts()->filter('Credit:GreaterThan', 0)->count()){
                $data = array(
                    'Type' => 'Convert',
                    'Credit' => $this->owner->Amount * $this->owner->Rate,
                    'Reference' => $this->owner->Reference,
                    'Description' => 'Account balance convert'
                );
				$id = $receive_account_class::create_statement($data, $this->owner->MemberID);
                $this->owner->ReceiveAccounts()->add($id);
            }
        }

		if($this->owner->isChanged('IsRevert') && $this->owner->IsRevert){
			foreach($this->owner->SendAccounts()->filter('Debit:GreaterThan', 0) as $account){
				$data = array(
                    'Type' => 'Convert',
                    'Credit' => $account->Debit,
                    'Reference' => $account->Reference,
                    'Description' => 'Revert account balance convert'
                );
				$id = $send_account_class::create_statement($data, $account->MemberID);
                $this->owner->SendAccounts()->add($id);
			}
			
			foreach($this->owner->ReceiveAccounts()->filter('Credit:GreaterThan', 0) as $account){
				$data = array(
                    'Type' => 'Convert',
                    'Debit' => $account->Credit,
                    'Reference' => $account->Reference,
                    'Description' => 'Revert account balance convert'
                );
				$id = $receive_account_class::create_statement($data, $account->MemberID);
                $this->owner->SendAccounts()->add($id);
			}
		}
    }

	function getRate(){
		return Config::inst()->get($this->class, sprintf('%s.Rate', $this->owner->class));
	}

	function getType(){
		$send_account = $this->getSendAccountClass();
		$receive_account = $this->getReceiveAccountClass();
		return _t('AccountConvert.TYPE_DESCRIPTION', 'Convert from {send_account} to {receive_account}', '', array('send_account' => singleton($send_account)->i18n_singular_name(), 'receive_account' => singleton($receive_account)->i18n_singular_name()));
	}
	
	function getSendAccountClass(){
		return Config::inst()->get($this->class, sprintf('%s.Send', $this->owner->class));
	}
	
	function getReceiveAccountClass(){
		return Config::inst()->get($this->class, sprintf('%s.Receive', $this->owner->class));
	}
	
	function canRevert($member = null) {
        return Permission::check('ALLOW_Revert') && !$this->owner->IsRevert;
    }
	
	function providePermissions() {
        return array(
            'ALLOW_Revert' => array(
                'name' => _t('AccountConvert.PERMISSION_REVERT', 'Allow revert access right'),
                'category' => _t('AccountConvert.PERMISSIONS_CATEGORY', 'Account Convert')
            )
        );
    }
}

class AccountConvert_Validator extends RequiredFields {

    protected $customRequired = array(
        'SetUsername',
        'Amount'
    );

    /**
     * Constructor
     */
    public function __construct() {
        $required = func_get_args();
        if(isset($required[0]) && is_array($required[0])) {
            $required = $required[0];
        }
        $required = array_merge($required, $this->customRequired);

        parent::__construct($required);
    }

    /**
     * Check if the submitted member data is valid (server-side)
     *
     * Check if a member with that email doesn't already exist, or if it does
     * that it is this member.
     *
     * @param array $data Submitted data
     * @return bool Returns TRUE if the submitted data is valid, otherwise
     *              FALSE.
     */
    function php($data) {
        $valid = parent::php($data);

        $amount = isset($data['Amount']) ? $data['Amount'] : 0;
        $memberid = isset($data['SetUsername']) ? Distributor::get_id_by_username($data['SetUsername']) : 0;
        $account_class = isset($data['AccountClass']) ? $data['AccountClass'] : '';

        // if we are in a complex table field popup, use ctf[childID], else use
        // ID
        if(isset($_REQUEST['ctf']['childID'])) {
            $id = $_REQUEST['ctf']['childID'];
        } elseif(isset($_REQUEST['ID'])) {
            $id = $_REQUEST['ID'];
        } else {
            $id = null;
        }

        if($amount <= 0) {
            $this->validationError('Amount', _t('AccountConvert.INVALID_CONVERT_AMOUNT', 'Invalid convert amount'), 'required');
            $valid = false;
        } else if($amount > 0 && $memberid) {
            $balance = Account::get($account_class, $memberid)->Balance;
            if($amount > $balance) {
                $this->validationError('Amount', _t('AccountConvert.INSUFFICIENT_BALANCE', 'Insufficient account balance ({balance})', 'Display insufficient account balance', array('balance' => Account::get($account_class, $memberid)->obj('Balance')->Nice())), 'required');
            }
            $valid = false;
        }

        return $valid;
    }

}
?>